import { $, cd, echo, chalk } from "zx";
import path from "path";
import stripBom from "strip-bom";
import fs from "fs";
import { NOW_DIR, TARGET_DIR } from "./CONST.mjs";
import XLSX from "xlsx";

class GitCommitClient {
  constructor(common, repositories) {
    this.common = common;
    this.repositories = repositories;
  }

  async analyseAllRepo() {
    for (const repo of this.repositories) {
      await this.analyseRepo(repo);
    }
  }

  async analyseRepo(repo) {
    const { startDate, endDate } = this.common;
    const { path } = repo;
    const projectInfo = {
      startDate,
      endDate,
      path,
    };
    await this.createRepoData(projectInfo);
  }

  async createRepoData(projectInfo) {
    // 进入git库路径
    await cd(projectInfo.path);

    // 从打印字符串提取repoName
    const repoName = await this.getRepoNameByRepoPath(projectInfo.path);
    // 拉取最新代码
    await this.fetchRepo(projectInfo.path);
    // 目标csv文件路径
    const targetFilePath = path.join(TARGET_DIR, `${repoName}.xlsx`);

    // 创建一个临时文件
    const tempFilePath = `${NOW_DIR}/temp-${repoName}.log`;
    const fd = fs.openSync(tempFilePath, "w");
    // 设置文件权限为 777
    fs.chmodSync(tempFilePath, 0o777);
    fs.closeSync(fd);
    // 将git信息放到临时文件

    let gitLogFlags = this.getGitLogFlags(
      projectInfo.startDate,
      projectInfo.endDate
    );
    await $`git log ${gitLogFlags} > ${tempFilePath}`;

    // 从临时文件提取数据
    const commitData = await this.extractCommitDataFromTempFile(
      tempFilePath,
      projectInfo,
      targetFilePath
    );
    await this.exportExcel(commitData, targetFilePath, repoName);
  }

  async exportExcel(commitData, targetFilePath, repoName) {
    // 写入文件的内容
    if (commitData && commitData.length > 0) {
      const contentStr = JSON.stringify(commitData);
      const contentList = JSON.parse(contentStr);
      // 创建工作簿
      const workbook = XLSX.utils.book_new();
      // 创建工作表
      const worksheet = XLSX.utils.json_to_sheet(contentList, {
        skipHeader: true,
      });
      // 将工作表添加到工作簿
      await XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");

      // 导出 Excel 文件
      await XLSX.writeFile(
        workbook,
        `${this.common.exportFilePath}/${repoName}.xlsx`
      );
    } else {
      echo`${chalk.bold.red(`没有提交数据`)}`;
    }
  }
  getGitLogFlags(startDate, endDate) {
    let gitLogFlags = [
      '--pretty=format:"@@@%H;%cn;%aE;%as"',
      "--numstat",
      "--date=iso",
      "--no-merges",
    ];
    if (startDate) {
      gitLogFlags.push(`--since="${startDate} 00:00:00"`);
    }
    if (endDate) {
      gitLogFlags.push(`--until="${endDate} 23:59:59"`);
    }
    return gitLogFlags;
  }

  async getRepoNameByRepoPath(path) {
    // 进入git库路径
    cd(path);
    // 打印仓库remote信息
    const gitRemoteOutPut = (await $`git remote -v`).stdout;
    let repoName;
    // 从打印字符串提取repoName
    if (gitRemoteOutPut.includes(".git")) {
      repoName = gitRemoteOutPut.substring(
        gitRemoteOutPut.lastIndexOf("/") + 1,
        gitRemoteOutPut.lastIndexOf(".git")
      );
    } else {
      repoName = gitRemoteOutPut.substring(
        gitRemoteOutPut.lastIndexOf("/") + 1,
        gitRemoteOutPut.lastIndexOf(" (push)")
      );
    }
    return repoName;
  }

  async fetchRepo(path) {
    // 进入git库路径
    cd(path);
    // 执行git pull
    await $`git pull`;
  }

  async extractCommitDataFromTempFile(
    tempFilePath,
    projectInfo,
    targetFilePath
  ) {
    const data = fs.readFileSync(tempFilePath, "utf8");
    const sanitizedData = stripBom(data.replace(/\0/g, ""));
    let splitStr = "\n";
    await cd(projectInfo.path);
    // 从打印字符串提取repoName
    const repoName = await this.getRepoNameByRepoPath(projectInfo.path);
    if (process.platform === "win32") {
      splitStr = "\r\n";
    }
    const lines = sanitizedData.split(splitStr);
    const result = [];
    result.push({
      contributorName: "提交人姓名",
      contributorEmail: "提交人邮箱",
      commitDate: "提交日期",
      addLines: "增加行",
      deleteLines: "减少行",
      repoName: "仓库名",
      id: "CommitId",
      projectCode: "项目ProjectCode",
    });
    for (const line of lines) {
      if (line.length > 0) {
        const regex = /^"?@@@/;
        const flag = regex.test(line);
        if (flag === true) {
          let value;
          if (process.platform === "win32") {
            value = line.slice(4).split(";");
          } else {
            value = line.slice(4, -1).split(";");
          }
          const commit = {
            projectCode: this.common.projectCode.toString(),
            contributorName: value[1],
            contributorEmail: value[2],
            commitDate: value[3],
            addLines: 0,
            deleteLines: 0,
            repoName: repoName,
            id: value[0],
          };
          result.push(commit);
        } else {
          const latestCommit = result[result.length - 1];
          if (latestCommit && line.match(/\d+/g)) {
            const [added, removed] = line.match(/\d+/g);
            latestCommit.addLines += parseInt(added);
            latestCommit.deleteLines += parseInt(removed);
          }
        }
      }
    }
    return result;
  }
}

export default GitCommitClient;
